
package at.fhj.ase.clientarch.client.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileWriter;
import java.io.IOException;
import java.rmi.RemoteException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import at.fhj.ase.clientarch.common.HardwareData;
import at.fhj.ase.clientarch.common.TransferObject;
import at.fhj.ase.clientarch.common.exception.BizException;
import at.fhj.ase.clientarch.common.util.CommonUtil;

import com.toedter.calendar.JDateChooser;

/**
 * The application's export dialog
 * 
 * @author cja, sfe | www.fh-joanneum.at | Client Architectures and Design
 */
public final class ExportDialog extends JDialog implements ActionListener {
  private static final long serialVersionUID = 7280782429565524040L;

  private final Logger logger = LoggerFactory.getLogger(getClass().getName());
  private final String ACTION_CMD_CBX = "actionCmdCbx";

  private JButton jBtnExport;
  private JLabel jLabelStart;
  private JLabel jLabelEnd;
  private JDateChooser jCalStart;
  private JDateChooser jCalEnd;
  private JCheckBox jCbxFromChart;

  private MainFrame mainGUI; // needed for export as chart data is read from there

  ExportDialog(MainFrame mainGUI) {
    super(mainGUI, true);

    this.mainGUI = mainGUI;
    initGUIComponents();
  }

  private void initGUIComponents() {
    this.setName("Export");
    this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);

    jLabelStart = new JLabel();
    jLabelEnd = new JLabel();
    jBtnExport = new JButton();
    jCalStart = new JDateChooser();
    jCalEnd = new JDateChooser();
    jCbxFromChart = new JCheckBox("Only use the data out of chart");

    jLabelStart.setText("Start Date:");
    jLabelStart.setName("jLabelStart");

    jLabelEnd.setText("End Date:");
    jLabelEnd.setName("jLabelEnd");

    jBtnExport.setText("Export");
    jBtnExport.setName("jBtnExport");
    jBtnExport.addActionListener(this);
    jBtnExport.setActionCommand(MainFrame.ACTION_CMD_EXPORT);

    jCbxFromChart.addActionListener(this);
    jCbxFromChart.setActionCommand(ACTION_CMD_CBX);

    javax.swing.GroupLayout thisLayout = new javax.swing.GroupLayout(this.getContentPane());
    this.getContentPane().setLayout(thisLayout);
    thisLayout
              .setHorizontalGroup(thisLayout
                                            .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                              .addGroup(
                                                        thisLayout
                                                                  .createSequentialGroup()
                                                                    .addContainerGap()
                                                                    .addGroup(
                                                                              thisLayout
                                                                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                                                          .addGroup(
                                                                                                    thisLayout
                                                                                                              .createSequentialGroup()
                                                                                                                .addComponent(jLabelStart)
                                                                                                                .addPreferredGap(
                                                                                                                                 javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                                                                                .addComponent(
                                                                                                                              jCalStart,
                                                                                                                              javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                                                                              143,
                                                                                                                              javax.swing.GroupLayout.PREFERRED_SIZE)
                                                                                                                .addPreferredGap(
                                                                                                                                 javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                                                                                .addComponent(jLabelEnd)
                                                                                                                .addPreferredGap(
                                                                                                                                 javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                                                                                .addComponent(
                                                                                                                              jCalEnd,
                                                                                                                              javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                                                                              143,
                                                                                                                              javax.swing.GroupLayout.PREFERRED_SIZE))
                                                                                          .addGroup(
                                                                                                    javax.swing.GroupLayout.Alignment.TRAILING,
                                                                                                    thisLayout
                                                                                                              .createSequentialGroup()
                                                                                                                .addComponent(jLabelStart)
                                                                                                                .addPreferredGap(
                                                                                                                                 javax.swing.LayoutStyle.ComponentPlacement.RELATED,
                                                                                                                                 12, Short.MAX_VALUE)
                                                                                                                .addComponent(jLabelEnd)
                                                                                                                .addPreferredGap(
                                                                                                                                 javax.swing.LayoutStyle.ComponentPlacement.RELATED,
                                                                                                                                 12, Short.MAX_VALUE)
                                                                                                                .addGroup(
                                                                                                                          thisLayout
                                                                                                                                    .createParallelGroup(
                                                                                                                                                         javax.swing.GroupLayout.Alignment.LEADING)
                                                                                                                                      .addComponent(
                                                                                                                                                    jCbxFromChart)
                                                                                                                                      .addComponent(jBtnExport))))
                                                                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)));
    thisLayout.setVerticalGroup(thisLayout
                                          .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                            .addGroup(
                                                      thisLayout
                                                                .createSequentialGroup()
                                                                  .addContainerGap()
                                                                  .addGroup(
                                                                            thisLayout
                                                                                      .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                                                        .addComponent(jLabelStart)
                                                                                        .addComponent(jCalStart, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                                                      javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                                                      javax.swing.GroupLayout.PREFERRED_SIZE)
                                                                                        .addComponent(jLabelEnd)
                                                                                        .addComponent(jCalEnd, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                                                                      javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                                                      javax.swing.GroupLayout.PREFERRED_SIZE))
                                                                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                                  .addGroup(
                                                                            thisLayout
                                                                                      .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                                                        .addComponent(jLabelStart))
                                                                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 12, Short.MAX_VALUE)
                                                                  .addComponent(jCbxFromChart)
                                                                  .addComponent(jBtnExport)
                                                                  .addContainerGap()));

  }

  @Override
  public final void actionPerformed(ActionEvent e) {
    final String actionCommand = e.getActionCommand();
    if (actionCommand.equals(MainFrame.ACTION_CMD_EXPORT)) {
      onExport();
    } else if (actionCommand.equals(ACTION_CMD_CBX)) {
      onCheckbox();
    }
  }

  /** Reverts the current state of the jDataChoosers concerning their visibility if the checkbox is clicked */
  private void onCheckbox() {
    jCalEnd.setEnabled(!jCalEnd.isEnabled());
    jCalStart.setEnabled(!jCalStart.isEnabled());
  }

  /**
   * Exports either the current data or the data from database to a file on local file system.<br>
   * This decision is based on the checkbox, whether it is selected or not.
   * <p>
   * In the end, the file location is displayed with a {@link JOptionPane}.<br>
   * There is no notification displayed if the user cancels the export.
   */
  private void onExport() {
    try {
      /* first thing before export dialog is displayed, we check whether we need to check for the date forms
       * we don't need to check for them if they are disabled by the checkbox */
      final boolean EXPORT_FROM_CHART = jCbxFromChart.isSelected();
      if (!EXPORT_FROM_CHART) {
        validateDates();
      }

      final String fileNameAbsolute = evaluateAbsoluteFileName(); // show the save-dialog in both cases, database and chart export
      if (fileNameAbsolute != null) {
        final List<HardwareData> dataList;
        if (EXPORT_FROM_CHART) {
          dataList = fetchDataFromChart();
        } else {
          dataList = fetchDataFromDatabase();
        }

        performExport(dataList, fileNameAbsolute);
        JOptionPane.showMessageDialog(this, "Data exported to: " + fileNameAbsolute);
      }

      this.setVisible(false);
      this.dispose();
    } catch (BizException be) {
      JOptionPane.showMessageDialog(this, be.getMessage(), "Error at export", JOptionPane.ERROR_MESSAGE);
    } catch (RemoteException re) {
      logger.error("Connection problem while trying to retrieve database data for export: ", re);
      JOptionPane.showMessageDialog(this, re.getMessage(), "Connection problem", JOptionPane.ERROR_MESSAGE);
    }
  }

  private String evaluateAbsoluteFileName() throws BizException {
    final JFileChooser fc = new JFileChooser();
    int saveResult = fc.showSaveDialog(this);
    if (saveResult == JFileChooser.APPROVE_OPTION) {
      return fc.getSelectedFile().getAbsolutePath();
    } else {
      return null;
    }
  }

  private void validateDates() throws BizException {
    final Date startDate = jCalStart.getDate();
    final Date endDate = jCalEnd.getDate();
    if (CommonUtil.areNull(startDate, endDate)) {
      throw new BizException("Incorrect dates (did you set dates at all?)");
    }

    final Timestamp start = new Timestamp(jCalStart.getDate().getTime());
    final Timestamp end = new Timestamp(jCalEnd.getDate().getTime());
    if (start.after(end)) {
      throw new BizException("Incorrect dates (start-date after end-date?)");
    }

    // TODO missing validation of dates (there is a maximum of e.g. seven days!)
  }

  private List<HardwareData> fetchDataFromDatabase() throws BizException, RemoteException {
    final Timestamp start = new Timestamp(jCalStart.getDate().getTime());
    final Timestamp end = new Timestamp(jCalEnd.getDate().getTime());
    final TransferObject dataFromServer = ClientController.INSTANCE.getHwComponent().getDataFromDB(start, end);
    if (CommonUtil.isNull(dataFromServer)) {
      throw new BizException("There are now rows in database for the specified dates");
    }

    return dataFromServer.getDataList();
  }

  private List<HardwareData> fetchDataFromChart() {
    return mainGUI.getCurrentChartData();
  }

  /**
   * Performs the actual file export into a CSV-file while processing the given {@link List} and setting start- and end-Timestamp.<br>
   * If the stardDate is <code>null</code>, "currentChar" will be used for file export string.<br>
   * All the other parameters <strong>must not</strong> be null!
   * 
   * @return a String which is the exact absolute filename on file system
   * @throws BizException if the export was unsuccessful
   */
  private void performExport(final List<HardwareData> dataList, final String fileNameAbsolute) throws BizException {
    try {
      final FileWriter writer = new FileWriter(fileNameAbsolute);

      final char SEP = ';';
      final String NEWLINE = System.getProperty("line.separator", "\n");

      // write the headline
      final StringBuilder sb = new StringBuilder(50); // the strings have about that length; saves performance
      writer.append("CPU [%]").append(SEP).append("RAM [%]").append(SEP).append("Time").append(NEWLINE);

      // write line for line and flush it inbetween to prevent any loss of data
      for (HardwareData hwData : dataList) {
        sb.append(hwData.getCpu()).append(SEP).append(hwData.getRam()).append(SEP).append(hwData.getTimestamp());
        writer.append(sb.toString()).append(NEWLINE);
        writer.flush();
        sb.setLength(0); // reset the StringBuilder preventing always creating a new instance (performance)
      }

      writer.close();
    } catch (IOException e) {
      logger.error("Could not write csv-file to filesystem: ", e);
      throw new BizException("There was a problem while exporting");
    }
  }
}
